Skip to content

gh-86533: Restore os.makedirs() ability to apply *mode* recursively#150011

Merged
gpshead merged 14 commits into
python:mainfrom
nessita:pr/23901
May 18, 2026
Merged

gh-86533: Restore os.makedirs() ability to apply *mode* recursively#150011
gpshead merged 14 commits into
python:mainfrom
nessita:pr/23901

Conversation

@nessita
Copy link
Copy Markdown
Contributor

@nessita nessita commented May 18, 2026

ZackerySpytz and others added 13 commits December 22, 2020 16:56
Allow os.makedirs() to apply the *mode* argument to any intermediate
directories that are created.
- Replace recursive_mode with more intuitive parent_mode parameter
- parent_mode=None (default): intermediate dirs use default permissions
- parent_mode=<mode>: intermediate dirs use specified permissions
- parent_mode=mode: matches Python 3.6 behavior
- Update documentation with version markers and usage examples
- Add comprehensive test coverage with separate focused test functions
- Fix test permissions to avoid cleanup issues (0o555 → 0o705)

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Add parent_mode parameter to Path.mkdir() for specifying intermediate
  directory permissions when parents=True
- Maintain pathlib's independence by using recursive implementation
  rather than delegating to os.makedirs
- Add comprehensive tests including umask behavior verification
- Update documentation and whatsnew entries
- Provides consistency with os.makedirs parent_mode parameter

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Use st_mode & 0o777 masking instead of stat.S_IMODE() to ignore
filesystem-specific bits like setgid, matching the approach used
in os.makedirs tests.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-authored-by: Zackery Spytz <zspytz@gmail.com>
Co-authored-by: Zackery Spytz <zspytz@gmail.com>
@python-cla-bot
Copy link
Copy Markdown

python-cla-bot Bot commented May 18, 2026

All commit authors signed the Contributor License Agreement.

CLA signed

@nessita
Copy link
Copy Markdown
Contributor Author

nessita commented May 18, 2026

@gpshead Hi! This is the PR we talked abut with the conflict fixed.

@read-the-docs-community
Copy link
Copy Markdown

read-the-docs-community Bot commented May 18, 2026

Documentation build overview

📚 cpython-previews | 🛠️ Build #32747194 | 📁 Comparing 6870753 against main (dbd8985)

  🔍 Preview build  

7 files changed · + 1 added · ± 6 modified

+ Added

± Modified

@gpshead gpshead self-assigned this May 18, 2026
@gpshead gpshead added the sprint label May 18, 2026
@github-project-automation github-project-automation Bot moved this to Todo in Sprint May 18, 2026
…dir tests

Address review feedback on the parent_mode addition:

- Docs (os.makedirs, Path.mkdir): state that parent_mode, like mode, is
  combined with the process umask; it does not bypass it.
- Mark the parameter as added in 3.15 (approved for the 3.15 backport)
  instead of "next", which on main would resolve to 3.16.
- Replace the misleading "parent_mode overrides umask" test with one
  that verifies umask IS applied to parent_mode (0o777 + umask 0o022 ->
  0o755); add the matching os.makedirs test.
- Make MakedirTests cleanup name-agnostic and robust via
  os_helper.rmtree() instead of a hard-coded dir1/.../dir6 walk.
- Align the new test_os skips with the pathlib tests
  (Emscripten/WASI/Android); restore the original 0o555 case in
  test_mode.
- Use os_helper.temp_umask() in the pathlib tests instead of a manual
  os.umask() try/finally.
@gpshead gpshead added needs backport to 3.15 pre-release feature fixes, bugs and security fixes type-feature A feature request or enhancement type-security A security issue type-bug An unexpected behavior, bug, or error labels May 18, 2026
@github-project-automation github-project-automation Bot moved this from Todo to In Progress in Sprint May 18, 2026
@gpshead gpshead enabled auto-merge (squash) May 18, 2026 22:49
@gpshead gpshead merged commit 9770e32 into python:main May 18, 2026
61 of 64 checks passed
@github-project-automation github-project-automation Bot moved this from In Progress to Done in Sprint May 18, 2026
@miss-islington-app
Copy link
Copy Markdown

Thanks @nessita for the PR, and @gpshead for merging it 🌮🎉.. I'm working now to backport this PR to: 3.15.
🐍🍒⛏🤖

@bedevere-app
Copy link
Copy Markdown

bedevere-app Bot commented May 18, 2026

GH-150036 is a backport of this pull request to the 3.15 branch.

@bedevere-app bedevere-app Bot removed the needs backport to 3.15 pre-release feature fixes, bugs and security fixes label May 18, 2026
@nessita nessita deleted the pr/23901 branch May 18, 2026 23:03
@bedevere-bot
Copy link
Copy Markdown

⚠️⚠️⚠️ Buildbot failure ⚠️⚠️⚠️

Hi! The buildbot AMD64 CentOS9 NoGIL 3.x (tier-1) has failed when building commit 9770e32.

What do you need to do:

  1. Don't panic.
  2. Check the buildbot page in the devguide if you don't know what the buildbots are or how they work.
  3. Go to the page of the buildbot that failed (https://buildbot.python.org/#/builders/1609/builds/5752) and take a look at the build logs.
  4. Check if the failure is related to this commit (9770e32) or if it is a false positive.
  5. If the failure is related to this commit, please, reflect that on the issue and make a new Pull Request with a fix.

You can take a look at the buildbot page here:

https://buildbot.python.org/#/builders/1609/builds/5752

Failed tests:

  • test_pathlib

Failed subtests:

  • test_mkdir_with_parent_mode - test.test_pathlib.test_pathlib.PathSubclassTest.test_mkdir_with_parent_mode
  • test_mkdir_parent_mode_deep_hierarchy - test.test_pathlib.test_pathlib.PathSubclassTest.test_mkdir_parent_mode_deep_hierarchy
  • test_mkdir_parent_mode_same_as_mode - test.test_pathlib.test_pathlib.PosixPathTest.test_mkdir_parent_mode_same_as_mode
  • test_mkdir_parent_mode_same_as_mode - test.test_pathlib.test_pathlib.PathTest.test_mkdir_parent_mode_same_as_mode
  • test_mkdir_parent_mode_deep_hierarchy - test.test_pathlib.test_pathlib.PathTest.test_mkdir_parent_mode_deep_hierarchy
  • test_mkdir_parent_mode_deep_hierarchy - test.test_pathlib.test_pathlib.PosixPathTest.test_mkdir_parent_mode_deep_hierarchy
  • test_mkdir_parent_mode_same_as_mode - test.test_pathlib.test_pathlib.PathSubclassTest.test_mkdir_parent_mode_same_as_mode
  • test_mkdir_with_parent_mode - test.test_pathlib.test_pathlib.PathTest.test_mkdir_with_parent_mode
  • test_mkdir_with_parent_mode - test.test_pathlib.test_pathlib.PosixPathTest.test_mkdir_with_parent_mode

Summary of the results of the build (if available):

==

Click to see traceback logs
Traceback (most recent call last):
  File "/home/buildbot/buildarea/3.x.itamaro-centos-aws.nogil/build/Lib/test/test_pathlib/test_pathlib.py", line 2534, in test_mkdir_with_parent_mode
    self.assertEqual(p.stat().st_mode & 0o777, 0o755)
    ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: 448 != 493


Traceback (most recent call last):
  File "/home/buildbot/buildarea/3.x.itamaro-centos-aws.nogil/build/Lib/test/test_pathlib/test_pathlib.py", line 2559, in test_mkdir_parent_mode_deep_hierarchy
    self.assertEqual(p.stat().st_mode & 0o777, 0o755)
    ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: 448 != 493


Traceback (most recent call last):
  File "/home/buildbot/buildarea/3.x.itamaro-centos-aws.nogil/build/Lib/test/test_pathlib/test_pathlib.py", line 2599, in test_mkdir_parent_mode_same_as_mode
    self.assertEqual(p.stat().st_mode & 0o777, 0o705)
    ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: 448 != 453

@bedevere-bot
Copy link
Copy Markdown

⚠️⚠️⚠️ Buildbot failure ⚠️⚠️⚠️

Hi! The buildbot ARM Raspbian 3.x (tier-3) has failed when building commit 9770e32.

What do you need to do:

  1. Don't panic.
  2. Check the buildbot page in the devguide if you don't know what the buildbots are or how they work.
  3. Go to the page of the buildbot that failed (https://buildbot.python.org/#/builders/424/builds/13744) and take a look at the build logs.
  4. Check if the failure is related to this commit (9770e32) or if it is a false positive.
  5. If the failure is related to this commit, please, reflect that on the issue and make a new Pull Request with a fix.

You can take a look at the buildbot page here:

https://buildbot.python.org/#/builders/424/builds/13744

Failed tests:

  • test_pathlib

Failed subtests:

  • test_mkdir_with_parent_mode - test.test_pathlib.test_pathlib.PathSubclassTest.test_mkdir_with_parent_mode
  • test_mkdir_parent_mode_deep_hierarchy - test.test_pathlib.test_pathlib.PathSubclassTest.test_mkdir_parent_mode_deep_hierarchy
  • test_mkdir_parent_mode_same_as_mode - test.test_pathlib.test_pathlib.PosixPathTest.test_mkdir_parent_mode_same_as_mode
  • test_mkdir_parent_mode_same_as_mode - test.test_pathlib.test_pathlib.PathTest.test_mkdir_parent_mode_same_as_mode
  • test_mkdir_parent_mode_deep_hierarchy - test.test_pathlib.test_pathlib.PathTest.test_mkdir_parent_mode_deep_hierarchy
  • test_mkdir_parent_mode_deep_hierarchy - test.test_pathlib.test_pathlib.PosixPathTest.test_mkdir_parent_mode_deep_hierarchy
  • test_mkdir_parent_mode_same_as_mode - test.test_pathlib.test_pathlib.PathSubclassTest.test_mkdir_parent_mode_same_as_mode
  • test_mkdir_with_parent_mode - test.test_pathlib.test_pathlib.PathTest.test_mkdir_with_parent_mode
  • test_mkdir_with_parent_mode - test.test_pathlib.test_pathlib.PosixPathTest.test_mkdir_with_parent_mode

Summary of the results of the build (if available):

==

Click to see traceback logs
Traceback (most recent call last):
  File "/var/lib/buildbot/workers/3.x.gps-raspbian.nondebug/build/Lib/test/test_pathlib/test_pathlib.py", line 2599, in test_mkdir_parent_mode_same_as_mode
    self.assertEqual(p.stat().st_mode & 0o777, 0o705)
    ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: 448 != 453


Traceback (most recent call last):
  File "/var/lib/buildbot/workers/3.x.gps-raspbian.nondebug/build/Lib/test/test_pathlib/test_pathlib.py", line 2534, in test_mkdir_with_parent_mode
    self.assertEqual(p.stat().st_mode & 0o777, 0o755)
    ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: 448 != 493


Traceback (most recent call last):
  File "/var/lib/buildbot/workers/3.x.gps-raspbian.nondebug/build/Lib/test/test_pathlib/test_pathlib.py", line 2559, in test_mkdir_parent_mode_deep_hierarchy
    self.assertEqual(p.stat().st_mode & 0o777, 0o755)
    ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: 448 != 493

@bedevere-bot
Copy link
Copy Markdown

⚠️⚠️⚠️ Buildbot failure ⚠️⚠️⚠️

Hi! The buildbot ARM64 MacOS M1 NoGIL 3.x (tier-2) has failed when building commit 9770e32.

What do you need to do:

  1. Don't panic.
  2. Check the buildbot page in the devguide if you don't know what the buildbots are or how they work.
  3. Go to the page of the buildbot that failed (https://buildbot.python.org/#/builders/1270/builds/7602) and take a look at the build logs.
  4. Check if the failure is related to this commit (9770e32) or if it is a false positive.
  5. If the failure is related to this commit, please, reflect that on the issue and make a new Pull Request with a fix.

You can take a look at the buildbot page here:

https://buildbot.python.org/#/builders/1270/builds/7602

Failed tests:

  • test_pathlib

Failed subtests:

  • test_mkdir_with_parent_mode - test.test_pathlib.test_pathlib.PathSubclassTest.test_mkdir_with_parent_mode
  • test_mkdir_parent_mode_deep_hierarchy - test.test_pathlib.test_pathlib.PathSubclassTest.test_mkdir_parent_mode_deep_hierarchy
  • test_mkdir_parent_mode_same_as_mode - test.test_pathlib.test_pathlib.PosixPathTest.test_mkdir_parent_mode_same_as_mode
  • test_mkdir_parent_mode_same_as_mode - test.test_pathlib.test_pathlib.PathTest.test_mkdir_parent_mode_same_as_mode
  • test_mkdir_parent_mode_deep_hierarchy - test.test_pathlib.test_pathlib.PathTest.test_mkdir_parent_mode_deep_hierarchy
  • test_mkdir_parent_mode_deep_hierarchy - test.test_pathlib.test_pathlib.PosixPathTest.test_mkdir_parent_mode_deep_hierarchy
  • test_mkdir_parent_mode_same_as_mode - test.test_pathlib.test_pathlib.PathSubclassTest.test_mkdir_parent_mode_same_as_mode
  • test_mkdir_with_parent_mode - test.test_pathlib.test_pathlib.PathTest.test_mkdir_with_parent_mode
  • test_mkdir_with_parent_mode - test.test_pathlib.test_pathlib.PosixPathTest.test_mkdir_with_parent_mode

Summary of the results of the build (if available):

==

Click to see traceback logs
Traceback (most recent call last):
  File "/Users/buildbot/buildarea/3.x.itamaro-macos-arm64-aws.macos-with-brew.nogil/build/Lib/test/test_pathlib/test_pathlib.py", line 2559, in test_mkdir_parent_mode_deep_hierarchy
    self.assertEqual(p.stat().st_mode & 0o777, 0o755)
    ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: 448 != 493


Traceback (most recent call last):
  File "/Users/buildbot/buildarea/3.x.itamaro-macos-arm64-aws.macos-with-brew.nogil/build/Lib/test/test_pathlib/test_pathlib.py", line 2534, in test_mkdir_with_parent_mode
    self.assertEqual(p.stat().st_mode & 0o777, 0o755)
    ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: 448 != 493


Traceback (most recent call last):
  File "/Users/buildbot/buildarea/3.x.itamaro-macos-arm64-aws.macos-with-brew.nogil/build/Lib/test/test_pathlib/test_pathlib.py", line 2599, in test_mkdir_parent_mode_same_as_mode
    self.assertEqual(p.stat().st_mode & 0o777, 0o705)
    ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: 448 != 453

gpshead added a commit that referenced this pull request May 19, 2026
…ively (GH-150011) (#150036)

bpo-42367: Restore os.makedirs() and pathlib.mkdir() ability to apply *mode* recursively via a new parent_mode= keyword argument.
(cherry picked from commit 9770e32)

+ Make Path.mkdir parent_mode tests umask-independent

test_mkdir_with_parent_mode, test_mkdir_parent_mode_deep_hierarchy and
test_mkdir_parent_mode_same_as_mode assert exact directory mode bits but
did not pin the process umask.  On buildbots running with a restrictive
umask (e.g. 0o077) the 0o755 leaf was masked down to 0o700, failing the
assertions.  Wrap them in os_helper.temp_umask(0o022), matching the
other umask-aware mkdir tests in this file.

---------

Co-authored-by: nessita <124304+nessita@users.noreply.github.com>
Co-authored-by: Zackery Spytz <zspytz@gmail.com>
Co-authored-by: Erlend E. Aasland <erlend@python.org>
Co-authored-by: Gregory P. Smith <greg@krypto.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

sprint type-bug An unexpected behavior, bug, or error type-feature A feature request or enhancement type-security A security issue

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

5 participants